package com.minelx.statementsplus.cha.base;

import com.alibaba.fastjson.JSONException;
import com.minelx.landisk.filesystem.core.File;
import com.minelx.util.multithread.processcontrol.SemaphoreHelper;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.StandardOpenOption;

import static com.minelx.landisk.filesystem.core.init.OpenPolicy.whatever;
import static java.nio.charset.StandardCharsets.UTF_8;

public class HttpResponseFetcher extends ResponseFetcher {

	static final int REST_MILLIS_BEFORE_RETRYING = 1000;

	static final int INIT_BVAL = -1;

	private static final int DEFAULT_RETRYING_COUNT = 10;

	private final ActualResponseFetcher actualResponseFetcher;

	// FIXME 2022/4/13 wait for fixing.     may use AOP instead
	private final SemaphoreHelper semaphoreHelper;

	public HttpResponseFetcher(ActualResponseFetcher actualResponseFetcher, SemaphoreHelper semaphoreHelper) {
		this.actualResponseFetcher = actualResponseFetcher;
		this.semaphoreHelper = semaphoreHelper;
	}

	@Override
	void extractToFile(File downloadedFile) {
		boolean anyContentPushed = fulfill(downloadedFile);
		// delete downloadedFile if no content written.
		if (!anyContentPushed) {
			downloadedFile.path().existence(whatever()).removeIfExists();
		}
	}

	private DoWorkResponse fetchOnce() {
		semaphoreHelper.acquire();
		DoWorkResponse result = DoWorkResponse.from(RetryingExecutor.retryingGet(
				actualResponseFetcher::next,
				DEFAULT_RETRYING_COUNT,
				JSONException.class));
		semaphoreHelper.release();
		return result;
	}

	boolean fulfill(OutputStream output) throws IOException, InterruptedException {
		DoWorkResponse doWorkResponse = fetchOnce();
		boolean everInnerWhile = false;
		while (!doWorkResponse.end()) {
			if (doWorkResponse.error()) { // failed flag 0
				System.err.println("respond a failed message: " + doWorkResponse.message());
				// break this loop while responding a failed message
				break;
			}

			// save result content
			String savedContent = preprocessContent(doWorkResponse, everInnerWhile);
			output.write(savedContent.getBytes(UTF_8));

			// save next bval
			everInnerWhile = true;
			// do next query
			doWorkResponse = fetchOnce();
		}
		return everInnerWhile;
	}

	private String preprocessContent(DoWorkResponse doWorkResponse, boolean everInnerWhile) {
		String responseContent = doWorkResponse.content();
		if (!everInnerWhile) { // init
			return responseContent;
		}
		return responseContent.substring(responseContent.indexOf("\n") + "\n".length());
	}

	private boolean fulfill(File downloadedFile) {
		try (OutputStream output = downloadedFile.outputStream(StandardOpenOption.APPEND)) {
			boolean anyContentPushed = fulfill(output);
			// flush content after fulfill
			output.flush();
			return anyContentPushed;
		} catch (IOException | InterruptedException e) {
			throw new RuntimeException("error while doing...", e);
		}
	}
}
